﻿using System.Collections.Generic;
using System.IO;
using System.Web.Hosting;
using System.Web.UI.WebControls;
using System.Xml;

namespace visinia.SmartEngine.Installer
{
    public class InstallController
    {
        public static string InstallModule(FileUpload FileUploadControl, bool SaveZip)
        {
            return Install(FileUploadControl, SaveZip, InstallType.module);            
        }

        public static string InstallPlugin(FileUpload FileUploadControl, bool SaveZip)
        {
            return Install(FileUploadControl, SaveZip, InstallType.plugin);
        }

        public static string InstallTheme(FileUpload FileUploadControl, bool SaveZip)
        {
            return Install(FileUploadControl, SaveZip, InstallType.theme);
        }

        public static string InstallModuleLocal(string ModuleName, string Package, string ZipFileName)
        {
            return InstallLocal(ModuleName, Package, ZipFileName, InstallType.module);
        }

        public static string InstallPluginLocal(string PluginName, string ZipFileName)
        {
            return InstallLocal(PluginName, null, ZipFileName, InstallType.plugin);
        }

        public static string InstallThemeLocal(string ThemeName, string ZipFileName)
        {
            return InstallLocal(ThemeName, null, ZipFileName, InstallType.theme);
        }

        public static bool UnInstallModule(int ModuleInstalledId)
        {
            return ModuleInstaller.UnInstallModule(ModuleInstalledId);
        }

        public static bool UnInstallTheme(string ThemeName)
        {
            return ThemeInstaller.UnInstallTheme(ThemeName);
        }

        public static bool UnInstallPlugin(int PluginId)
        {
            return PluginInstaller.UnInstallPlugin(PluginId);
        }

        public static string RemoveInstallerZip(string ZipFileName)
        {
            // remove the zip file from install folder was saved for later installtions
            string bkFolder = HostingEnvironment.MapPath(WebRoots.GetInstallRoot);
            File.Delete(bkFolder + ZipFileName);
            // remove content file also
            // save with the same name as the zip file
            string newContetFileName = bkFolder + ZipFileName + ".content";
            File.Delete(newContetFileName);
            return "Zip File Deleted";
        }

        private enum InstallType
        {
            /// <summary>
            /// Install modules
            /// </summary>
            module,
            /// <summary>
            /// Install plugins and modules
            /// </summary>
            plugin,
            /// <summary>
            /// Install theme
            /// </summary>
            theme
        }

        #region Installer
        private static string Install(FileUpload FileUploadControl, bool SaveZip, InstallType installType)
        {
            if (FileUploadControl.HasFile)
            {
                string ZipFileName = FileUploadControl.FileName;
                if (ZipFileName.EndsWith(".zip"))
                {
                    string tempInstallFolder = HostingEnvironment.MapPath(WebRoots.GetInstallTemporaryRoot);
                    // clean temporary folder before using
                    if (Directory.Exists(tempInstallFolder))
                        Directory.Delete(tempInstallFolder, true);
                    Directory.CreateDirectory(tempInstallFolder);
                    
                    // save the zip file to the temporary install folder
                    string zipTempPath = tempInstallFolder + ZipFileName;
                    FileUploadControl.SaveAs(zipTempPath);

                    // extract files to temporary install folder                    
                    if (!ZipController.ExtractZip(zipTempPath, tempInstallFolder))  
                        return SafeExit("Not a Valid Zip File");

                    // find visinia installer file(s) (e.g. abcde.vi)
                    string[] viFiles = Directory.GetFiles(tempInstallFolder, "*.vi", SearchOption.AllDirectories);
                    // find visinia installer zip content file (e.g. abcde.content)                    
                    string[] ContentFiles = Directory.GetFiles(tempInstallFolder, "*.content");

                    // error: no content file
                    if (ContentFiles.Length == 0)                    
                        return SafeExit("Can't Find xx.content File");                    
                    // error: multiple content files are not allowed
                    if (ContentFiles.Length > 1)                            
                        return SafeExit("More Than One xx.content File");                    
                    // error: no vi file
                    if (viFiles.Length == 0)                            
                        return SafeExit("Can't Find xx.vi File");                   

                    List<ContentFileInfo> ContentFileList = ContentFileParser.ParseContentFile(ContentFiles[0]);

                    foreach (string viFile in viFiles)
                    {
                        ParseInstallerFile(viFile, tempInstallFolder, ContentFileList, installType);
                    }

                    if (SaveZip)
                    {
                        // save the zip file to install folder so for later installtion user not to upload the zip again                    
                        string bkFolder = HostingEnvironment.MapPath(WebRoots.GetInstallRoot);
                        File.Copy(zipTempPath, bkFolder + ZipFileName, true);
                        // save content file also
                        // save with the same name as the zip file this will make search easy for local install
                        string newContetFileName = bkFolder + ZipFileName + ".content";
                        File.Copy(ContentFiles[0], newContetFileName, true);
                    }    
                    return SafeExit("Installed Properly");
                }
            }
            return "Error Occured During Installation";
        }

        private static string SafeExit(string message)
        {            
            // remove temporary install folder
            Directory.Delete(HostingEnvironment.MapPath(WebRoots.GetInstallTemporaryRoot), true);
            return message;
        }
        #endregion        
        
        #region Local Installer
        private static string InstallLocal(string Name, string Package, string ZipFileName, InstallType installType)
        {
            if (ZipFileName.EndsWith(".zip"))
            {
                string FilePath = HostingEnvironment.MapPath(WebRoots.GetInstallRoot) + ZipFileName;
                if (File.Exists(FilePath))
                {
                    // extract files to temporary install folder
                    string tempInstallFolder = HostingEnvironment.MapPath(WebRoots.GetInstallTemporaryRoot);
                    // clean temporary folder before using
                    if(Directory.Exists(tempInstallFolder))
                        Directory.Delete(tempInstallFolder, true);
                    Directory.CreateDirectory(tempInstallFolder);
                    
                    // extract files to temporary install folder                    
                    if (!ZipController.ExtractZip(FilePath, tempInstallFolder))                    
                        return SafeExit("Not a Valid Zip File");                    

                    // find visinia installer file(s) (e.g. abcde.vi)
                    string[] viFiles = Directory.GetFiles(tempInstallFolder, "*.vi", SearchOption.AllDirectories);
                                       
                    string ContentFilePath = FilePath + ".content";
                    // find visinia installer zip content file (e.g. abcde.content)
                    if (!File.Exists(ContentFilePath))
                        return SafeExit("Can't Find xx.content File");
                    
                    List<ContentFileInfo> ContentFileList = new List<ContentFileInfo>();
                    
                    // install only the user selected module but not all                    
                    if (installType == InstallType.module)
                    {
                        List<ContentFileInfo> ContentFileListTemp = new List<ContentFileInfo>();
                        ContentFileListTemp = ContentFileParser.ParseContentFile(ContentFilePath);
                        ContentFileList.Add(ContentFileListTemp.Find(
                            p => (string.Compare(p.Name, Name, true) == 0) &&
                                 (string.Compare(p.Package, Package, true) == 0)&&
                                 (p.IsInstalled == false) &&
                                 (p.Type == ContentFileType.Module)));
                    }

                    // for plugins install all modules as well as the plugin itself.
                    // it will be just annoying to ask user to intall first the plugin 
                    // then to install all modules for that plugins, so just install every thing
                    if (installType == InstallType.plugin)
                    {
                        ContentFileList = ContentFileParser.ParseContentFile(ContentFilePath);
                    }

                    // install only the user selected theme but not all                    
                    if (installType == InstallType.theme)
                    {
                        List<ContentFileInfo> ContentFileListTemp = new List<ContentFileInfo>();
                        ContentFileListTemp = ContentFileParser.ParseContentFile(ContentFilePath);
                        ContentFileList.Add(ContentFileListTemp.Find(
                            p => (string.Compare(p.Name, Name, true) == 0) &&                                 
                                 (p.IsInstalled == false) &&
                                 (p.Type == ContentFileType.Theme)));
                    }

                    foreach (string viFile in viFiles)
                    {
                        // what if error occures here
                        ParseInstallerFile(viFile, tempInstallFolder, ContentFileList, installType);
                    }                    
                    return SafeExit("Installed Properly");
                }
            }
            return "Error Occured During Installation";
        }        
        #endregion

        #region Installer File Parser
        private static bool ParseInstallerFile(string viFile, string tempInstallFolder, List<ContentFileInfo> ContentFileList, InstallType installType)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(viFile);
            XmlNode visiniaNode = doc.DocumentElement;
            if (visiniaNode != null)
            {
                // TODO check visinia version 
                
                // this is plugin installation data
                XmlNode PluginTypeNode = visiniaNode["plugin"];
                if (PluginTypeNode != null)
                {
                    if (installType == InstallType.plugin)
                        return PluginInstaller.HandlePlugin(viFile, ContentFileList, doc, PluginTypeNode);
                }
                // this is module installation data
                XmlNode moduleTypeNode = visiniaNode["module"];
                if (moduleTypeNode != null)
                {
                    if ((installType == InstallType.module) || (installType == InstallType.plugin))
                        return ModuleInstaller.HandleModule(viFile, ContentFileList, doc, moduleTypeNode);
                }
                // this is plugin installation data
                XmlNode ThemeTypeNode = visiniaNode["theme"];
                if (ThemeTypeNode != null)
                {
                    if (installType == InstallType.theme)
                        return ThemeInstaller.HandleTheme(viFile, ContentFileList, doc, ThemeTypeNode);
                }
            }
            return false;
        }   	 
        #endregion           
    }
}
